버퍼 오버플로
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
버퍼 오버플로는 할당된 메모리 공간을 초과하여 데이터를 쓰는 오류로, 프로그램의 보안 취약점을 야기한다. 이는 불충분한 경계 검사로 인해 발생하며, 문자열 복사 시 흔히 나타난다. 버퍼 오버플로를 악용하면 프로그램의 실행 흐름을 변경하거나, 시스템의 권한을 탈취하는 등의 공격이 가능하다. 스택 기반, 힙 기반 등 다양한 방식으로 악용될 수 있으며, NOP 슬라이드, 레지스터로 건너뛰기 등의 기술이 사용된다.
C/C++는 버퍼 오버플로에 취약하며, 안전한 프로그래밍 언어 선택, 안전한 라이브러리 사용, 버퍼 오버플로 보호 기법, 실행 공간 보호, 주소 공간 배치 난수화 등의 대응 수단이 존재한다. 심층 패킷 조사를 통해 공격 시도를 탐지할 수도 있다. 버퍼 오버플로 개념은 1972년에 처음 소개되었으며, 모리스 웜, 코드 레드 웜, SQL 슬래머 웜 등 여러 악성 코드에서 악용되었다.
더 읽어볼만한 페이지
- 소프트웨어 버그 - 교착 상태
교착 상태는 둘 이상의 프로세스가 자원을 점유하고 서로의 자원을 요청하여 더 이상 진행할 수 없는 상태를 의미하며, 상호 배제, 점유 대기, 비선점, 순환 대기 네 가지 조건이 모두 충족되어야 발생하고, 운영 체제는 이를 예방, 회피, 무시, 발견하는 방법으로 관리한다. - 소프트웨어 버그 - 글리치
글리치는 예기치 않은 오작동이나 오류를 뜻하며, 전자 공학, 컴퓨터, 비디오 게임, 텔레비전 방송, 대중문화 등 다양한 분야에서 기능 실패, 오류, 그래픽 및 사운드 문제, 신호 오류 등의 이상 현상을 포괄적으로 지칭하는 용어이다. - 취약점 공격 - 보안 취약점
보안 취약점은 시스템의 설계, 구현, 운영, 관리상 결함이나 약점으로, 위협에 의해 악용되어 시스템 보안 정책을 위반할 수 있는 요소이며, ISO 27005, IETF RFC 4949, NIST SP 800-30, ENISA 등 다양한 기관에서 정의하고 있다. - 취약점 공격 - 인터넷 보안
인터넷 보안은 사이버 위협, 악성 소프트웨어, 서비스 거부 공격 등으로부터 정보와 시스템을 보호하기 위해 네트워크 계층 보안, 다단계 인증, 방화벽 등 다양한 기술과 방법을 포괄한다. - 컴퓨터 메모리 - 플래시 메모리
플래시 메모리는 전기적으로 데이터의 쓰기 및 삭제가 가능한 비휘발성 메모리 기술로, 마스오카 후지오 박사가 발명하여 카메라 플래시와 유사한 소거 방식으로 인해 명명되었으며, NOR형과 NAND형으로 나뉘어 각기 다른 분야에 적용된다. - 컴퓨터 메모리 - 메모리 계층 구조
메모리 계층 구조는 CPU 데이터 접근 속도 향상을 위해 레지스터, 캐시, RAM, 보조 기억 장치 등으로 구성되며, 속도, 용량, 비용이 다른 계층들을 통해 효율적인 메모리 관리를 가능하게 한다.
버퍼 오버플로 | |
---|---|
보안 취약점 | |
종류 | 소프트웨어 보안 취약점 |
발견 시기 | 1972년 |
악용 방법 | 서비스 거부 공격 임의 코드 실행 권한 상승 |
일반적인 약점 열거 | CWE-119: 버퍼에 할당된 공간 내에서 작업 제한의 부적절한 제한 ('고전적인 버퍼 오버플로') |
Common Vulnerabilities and Exposures (CVE) | CVE-2019-14287 CVE-2019-14288 CVE-2019-14899 CVE-2019-14900 CVE-2019-14901 CVE-2019-15845 CVE-2019-15903 CVE-2019-16276 CVE-2019-16277 CVE-2019-16719 CVE-2019-17006 CVE-2019-17007 CVE-2019-17008 CVE-2019-17009 CVE-2019-17010 CVE-2019-17011 CVE-2019-17012 CVE-2019-17013 CVE-2019-17014 CVE-2019-17015 CVE-2019-17016 CVE-2019-17017 CVE-2019-17018 CVE-2019-17019 CVE-2019-17020 CVE-2019-17021 CVE-2019-17022 CVE-2019-17023 CVE-2019-17024 CVE-2019-17025 CVE-2019-17026 CVE-2019-17027 CVE-2019-17028 CVE-2019-17029 CVE-2019-17030 CVE-2019-17031 CVE-2019-17032 CVE-2019-17033 CVE-2019-17034 CVE-2019-17035 CVE-2019-17036 CVE-2019-17037 CVE-2019-17038 CVE-2019-17039 CVE-2019-17040 CVE-2019-17041 CVE-2019-17042 CVE-2019-17043 CVE-2019-17044 CVE-2019-17045 CVE-2019-17046 CVE-2019-17047 CVE-2019-17048 CVE-2019-17049 CVE-2019-17050 CVE-2019-17051 CVE-2019-17052 CVE-2019-17053 CVE-2019-17054 CVE-2019-17055 CVE-2019-17056 CVE-2019-17057 CVE-2019-17058 CVE-2019-17059 CVE-2019-17060 CVE-2019-17061 CVE-2019-17062 CVE-2019-17063 CVE-2019-17064 CVE-2019-17065 CVE-2019-17066 CVE-2019-17067 CVE-2019-17068 CVE-2019-17069 CVE-2019-17070 CVE-2019-17071 CVE-2019-17072 CVE-2019-17073 CVE-2019-17074 CVE-2019-17075 CVE-2019-17076 CVE-2019-17077 CVE-2019-17078 CVE-2019-17079 CVE-2019-17080 CVE-2019-17081 CVE-2019-17082 CVE-2019-17083 CVE-2019-17084 CVE-2019-17085 CVE-2019-17086 CVE-2019-17087 CVE-2019-17088 CVE-2019-17089 CVE-2019-17090 CVE-2019-17091 CVE-2019-17092 CVE-2019-17093 CVE-2019-17094 CVE-2019-17095 CVE-2019-17096 CVE-2019-17097 CVE-2019-17098 CVE-2019-17099 CVE-2019-17100 CVE-2019-17101 CVE-2019-17102 CVE-2019-17103 CVE-2019-17104 CVE-2019-17105 CVE-2019-17106 CVE-2019-17107 CVE-2019-17108 CVE-2019-17109 CVE-2019-17110 CVE-2019-17111 CVE-2019-17112 CVE-2019-17113 CVE-2019-17114 CVE-2019-17115 CVE-2019-17116 CVE-2019-17117 CVE-2019-17118 CVE-2019-17119 CVE-2019-17120 CVE-2019-17121 CVE-2019-17122 CVE-2019-17123 CVE-2019-17124 CVE-2019-17125 CVE-2019-17126 CVE-2019-17127 CVE-2019-17128 CVE-2019-17129 CVE-2019-17130 CVE-2019-17131 CVE-2019-17132 CVE-2019-17133 CVE-2019-17134 CVE-2019-17135 CVE-2019-17136 CVE-2019-17137 CVE-2019-17138 CVE-2019-17139 CVE-2019-17140 CVE-2019-17141 CVE-2019-17142 CVE-2019-17143 CVE-2019-17144 CVE-2019-17145 CVE-2019-17146 CVE-2019-17147 CVE-2019-17148 CVE-2019-17149 CVE-2019-17150 CVE-2019-17151 CVE-2019-17152 CVE-2019-17153 CVE-2019-17154 CVE-2019-17155 CVE-2019-17156 CVE-2019-17157 CVE-2019-17158 CVE-2019-17159 CVE-2019-17160 CVE-2019-17161 CVE-2019-17162 CVE-2019-17163 CVE-2019-17164 CVE-2019-17165 CVE-2019-17166 CVE-2019-17167 CVE-2019-17168 CVE-2019-17169 CVE-2019-17170 CVE-2019-17171 CVE-2019-17172 CVE-2019-17173 CVE-2019-17174 CVE-2019-17175 CVE-2019-17176 CVE-2019-17177 CVE-2019-17178 CVE-2019-17179 CVE-2019-17180 CVE-2019-17181 CVE-2019-17182 CVE-2019-17183 CVE-2019-17184 CVE-2019-17185 CVE-2019-17186 CVE-2019-17187 CVE-2019-17188 CVE-2019-17189 CVE-2019-17190 CVE-2019-17191 CVE-2019-17192 CVE-2019-17193 CVE-2019-17194 CVE-2019-17195 CVE-2019-17196 CVE-2019-17197 CVE-2019-17198 CVE-2019-17199 CVE-2019-17200 CVE-2019-17201 CVE-2019-17202 CVE-2019-17203 CVE-2019-17204 CVE-2019-17205 CVE-2019-17206 CVE-2019-17207 CVE-2019-17208 CVE-2019-17209 CVE-2019-17210 CVE-2019-17211 CVE-2019-17212 CVE-2019-17213 CVE-2019-17214 CVE-2019-17215 CVE-2019-17216 CVE-2019-17217 CVE-2019-17218 CVE-2019-17219 CVE-2019-17220 CVE-2019-17221 CVE-2019-17222 CVE-2019-17223 CVE-2019-17224 CVE-2019-17225 CVE-2019-17226 CVE-2019-17227 CVE-2019-17228 CVE-2019-17229 CVE-2019-17230 CVE-2019-17231 CVE-2019-17232 CVE-2019-17233 CVE-2019-17234 CVE-2019-17235 CVE-2019-17236 CVE-2019-17237 CVE-2019-17238 CVE-2019-17239 CVE-2019-17240 CVE-2019-17241 CVE-2019-17242 CVE-2019-17243 CVE-2019-17244 CVE-2019-17245 CVE-2019-17246 CVE-2019-17247 CVE-2019-17248 CVE-2019-17249 CVE-2019-17250 CVE-2019-17251 CVE-2019-17252 CVE-2019-17253 CVE-2019-17254 CVE-2019-17255 CVE-2019-17256 CVE-2019-17257 CVE-2019-17258 CVE-2019-17259 CVE-2019-17260 CVE-2019-17261 CVE-2019-17262 CVE-2019-17263 CVE-2019-17264 CVE-2019-17265 CVE-2019-17266 CVE-2019-17267 CVE-2019-17268 CVE-2019-17269 CVE-2019-17270 CVE-2019-17271 CVE-2019-17272 CVE-2019-17273 CVE-2019-17274 CVE-2019-17275 CVE-2019-17276 CVE-2019-17277 CVE-2019-17278 CVE-2019-17279 CVE-2019-17280 CVE-2019-17281 CVE-2019-17282 CVE-2019-17283 CVE-2019-17284 CVE-2019-17285 CVE-2019-17286 CVE-2019-17287 CVE-2019-17288 CVE-2019-17289 CVE-2019-17290 CVE-2019-17291 CVE-2019-17292 CVE-2019-17293 CVE-2019-17294 CVE-2019-17295 CVE-2019-17296 CVE-2019-17297 CVE-2019-17298 CVE-2019-17299 CVE-2019-17300 CVE-2019-17301 CVE-2019-17302 CVE-2019-17303 CVE-2019-17304 CVE-2019-17305 CVE-2019-17306 CVE-2019-17307 CVE-2019-17308 CVE-2019-17309 CVE-2019-17310 CVE-2019-17311 CVE-2019-17312 CVE-2019-17313 CVE-2019-17314 CVE-2019-17315 CVE-2019-17316 CVE-2019-17317 CVE-2019-17318 CVE-2019-17319 CVE-2019-17320 CVE-2019-17321 CVE-2019-17322 CVE-2019-17323 CVE-2019-17324 CVE-2019-17325 CVE-2019-17326 CVE-2019-17327 CVE-2019-17328 CVE-2019-17329 CVE-2019-17330 CVE-2019-17331 CVE-2019-17332 CVE-2019-17333 CVE-2019-17334 CVE-2019-17335 CVE-2019-17336 CVE-2019-17337 CVE-2019-17338 CVE-2019-17339 CVE-2019-17340 CVE-2019-17341 CVE-2019-17342 CVE-2019-17343 CVE-2019-17344 CVE-2019-17345 CVE-2019-17346 CVE-2019-17347 CVE-2019-17348 CVE-2019-17349 CVE-2019-17350 CVE-2019-17351 CVE-2019-17352 CVE-2019-17353 CVE-2019-17354 CVE-2019-17355 CVE-2019-17356 CVE-2019-17357 CVE-2019-17358 CVE-2019-17359 CVE-2019-17360 CVE-2019-17361 CVE-2019-17362 CVE-2019-17363 CVE-2019-17364 CVE-2019-17365 CVE-2019-17366 CVE-2019-17367 CVE-2019-17368 CVE-2019-17369 CVE-2019-17370 CVE-2019-17371 CVE-2019-17372 CVE-2019-17373 CVE-2019-17374 CVE-2019-17375 CVE-2019-17376 CVE-2019-17377 CVE-2019-17378 CVE-2019-17379 CVE-2019-17380 CVE-2019-17381 CVE-2019-17382 CVE-2019-17383 CVE-2019-17384 CVE-2019-17385 CVE-2019-17386 CVE-2019-17387 CVE-2019-17388 CVE-2019-17389 CVE-2019-17390 CVE-2019-17391 CVE-2019-17392 CVE-2019-17393 CVE-2019-17394 CVE-2019-17395 CVE-2019-17396 CVE-2019-17397 CVE-2019-17398 CVE-2019-17399 CVE-2019-17400 CVE-2019-17401 CVE-2019-17402 CVE-2019-17403 CVE-2019-17404 CVE-2019-17405 CVE-2019-17406 CVE-2019-17407 CVE-2019-17408 CVE-2019-17409 CVE-2019-17410 CVE-2019-17411 CVE-2019-17412 CVE-2019-17413 CVE-2019-17414 CVE-2019-17415 CVE-2019-17416 CVE-2019-17417 CVE-2019-17418 CVE-2019-17419 CVE-2019-17420 CVE-2019-17421 CVE-2019-17422 CVE-2019-17423 CVE-2019-17424 CVE-2019-17425 CVE-2019-17426 CVE-2019-17427 CVE-2019-17428 CVE-2019-17429 CVE-2019-17430 CVE-2019-17431 CVE-2019-17432 CVE-2019-17433 CVE-2019-17434 CVE-2019-17435 CVE-2019-17436 CVE-2019-17437 CVE-2019-17438 CVE-2019-17439 CVE-2019-17440 CVE-2019-17441 CVE-2019-17442 CVE-2019-17443 CVE-2019-17444 CVE-2019-17445 CVE-2019-17446 CVE-2019-17447 CVE-2019-17448 CVE-2019-17449 CVE-2019-17450 CVE-2019-17451 CVE-2019-17452 CVE-2019-17453 CVE-2019-17454 CVE-2019-17455 CVE-2019-17456 CVE-2019-17457 CVE-2019-17458 CVE-2019-17459 CVE-2019-17460 CVE-2019-17461 CVE-2019-17462 CVE-2019-17463 CVE-2019-17464 CVE-2019-17465 CVE-2019-17466 CVE-2019-17467 CVE-2019-17468 CVE-2019-17469 CVE-2019-17470 CVE-2019-17471 CVE-2019-17472 CVE-2019-17473 CVE-2019-17474 CVE-2019-17475 CVE-2019-17476 CVE-2019-17477 CVE-2019-17478 CVE-2019-17479 CVE-2019-17480 CVE-2019-17481 CVE-2019-17482 CVE-2019-17483 CVE-2019-17484 CVE-2019-17485 CVE-2019-17486 CVE-2019-17487 CVE-2019-17488 CVE-2019-17489 CVE-2019-17490 CVE-2019-17491 CVE-2019-17492 CVE-2019-17493 CVE-2019-17494 CVE-2019-17495 CVE-2019-17496 CVE-2019-17497 CVE-2019-17498 CVE-2019-17499 CVE-2019-17500 CVE-2019-17501 CVE-2019-17502 CVE-2019-17503 CVE-2019-17504 CVE-2019-17505 CVE-2019-17506 CVE-2019-17507 CVE-2019-17508 CVE-2019-17509 CVE-2019-17510 CVE-2019-17511 CVE-2019-17512 CVE-2019-17513 CVE-2019-17514 CVE-2019-17515 CVE-2019-17516 CVE-2019-17517 CVE-2019-17518 CVE-2019-17519 CVE-2019-17520 CVE-2019-17521 CVE-2019-17522 CVE-2019-17523 CVE-2019-17524 CVE-2019-17525 CVE-2019-17526 CVE-2019-17527 CVE-2019-17528 CVE-2019-17529 CVE-2019-17530 CVE-20 |
2. 기술적인 설명
버퍼 오버플로는 불충분한 경계 검사에 의해 버퍼에 쓰인 데이터가 버퍼에 이미 할당된 근접한 메모리 주소에 있는 데이터 값을 오염시킬 때 발생한다. 대부분 이는 문자열을 하나의 버퍼에서 다른 버퍼로 복사할 때 발생한다.
컴퓨터 프로그램을 만들 때, 고정 길이의 버퍼라고 불리는 영역을 확보하여 거기에 데이터를 저장하는 기법이 자주 사용된다.
예를 들어, 이메일주소는 200자를 넘지 않을 것이라고 예상하고
# 200자 분량의 영역을 버퍼로 준비한다.
# 사용자가 200자보다 긴 이메일 주소를 입력한다.
# 프로그램이 버퍼의 크기를 확인하지 않고 입력 데이터를 덮어쓴다.
# 버퍼로 확보한 영역을 벗어나 데이터가 덮어씌워진다.
이것이 버퍼 오버플로이다. 가령 덮어씌워진 부분에 프로그램의 동작상 의미가 있는 데이터가 있다면, 이를 덮어쓰고 파괴함으로써 프로그램은 사용자의 의도와 다른 동작을 보일 것이다.
이처럼 버퍼 오버플로는, 프로그램이 준비한 버퍼의 크기를 초과하여 데이터를 덮어쓰는 버그이다.
==== 기본 예제 ====
메모리 상에 인접한 8바이트 문자열 버퍼 A와 2바이트 정수 B가 있을 때, A의 크기를 초과하는 문자열을 A에 복사하면 B의 값이 의도치 않게 변경될 수 있다. 예를 들어, "excessive"와 같이 A의 크기보다 큰 문자열을 복사하면 B를 덮어쓰게 된다.
아래는 C 언어로 작성된 예제이다. 프로그램은 메모리에서 인접한 두 개의 변수를 갖는다. 하나는 8바이트 길이의 문자열 버퍼 A이고, 다른 하나는 2바이트 빅 엔디안 정수 B이다.
```text
char A[8] = "";
unsigned short B = 1979;
```
처음 A는 0바이트만 포함하고, B는 숫자 1979를 포함한다.
변수 이름 | A | B | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
값 | 1979 | |||||||||
16진수 값 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 07 | BB |
프로그램이 A 버퍼에 널 종료 문자열 "excessive"를 ASCII 인코딩으로 저장하려고 시도할때, "excessive"는 9글자 길이이며 널 종료 문자를 포함하여 10바이트로 인코딩되지만 A는 8바이트만 사용할 수 있다. 문자열의 길이를 확인하지 못하여 B의 값도 덮어씁니다.
```text
strcpy(A, "excessive");
```
변수 이름 | A | B | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
값 | e | x | c | e | s | s | i | v | 25856 | |
16진수 | 65 | 78 | 63 | 65 | 73 | 73 | 69 | 76 | 65 | 00 |
B의 값은 의도치 않게 문자열의 일부로 형성된 숫자로 대체되었다. 이 예에서 "e" 다음에 0 바이트가 오면 25856이 된다.
할당된 메모리의 끝을 지나 데이터를 쓰는 것은 때때로 운영 체제에 의해 감지되어 프로세스를 종료하는 세그먼테이션 오류 오류를 생성할 수 있다.
이러한 버퍼 오버플로를 방지하기 위해, `strcpy` 대신 `strlcpy` 함수를 사용하여 최대 용량을 제한할 수 있다.
```text
strlcpy(A, "excessive", sizeof(A));
```
`strlcpy`는 `strncpy`보다 안전하지만, 소스 문자열의 길이가 버퍼 크기보다 크거나 같은 경우 대상 버퍼를 널로 종료하지 않을 수 있으므로 주의해야 한다.
2. 1. 기본 예제
메모리 상에 인접한 8바이트 문자열 버퍼 A와 2바이트 정수 B가 있을 때, A의 크기를 초과하는 문자열을 A에 복사하면 B의 값이 의도치 않게 변경될 수 있다. 예를 들어, "excessive"와 같이 A의 크기보다 큰 문자열을 복사하면 B를 덮어쓰게 된다.아래는 C 언어로 작성된 예제이다. 프로그램은 메모리에서 인접한 두 개의 변수를 갖는다. 하나는 8바이트 길이의 문자열 버퍼 A이고, 다른 하나는 2바이트 빅 엔디안 정수 B이다.
```text
char A[8] = "";
unsigned short B = 1979;
```
처음 A는 0바이트만 포함하고, B는 숫자 1979를 포함한다.
변수 이름 | A | B | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
값 | [ | 1979 | ||||||||
16진수 값 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 07 | BB |
프로그램이 A 버퍼에 널 종료 문자열 "excessive"를 ASCII 인코딩으로 저장하려고 시도할때, "excessive"는 9글자 길이이며 널 종료 문자를 포함하여 10바이트로 인코딩되지만 A는 8바이트만 사용할 수 있다. 문자열의 길이를 확인하지 못하여 B의 값도 덮어씁니다.
```text
strcpy(A, "excessive");
```
변수 이름 | A | B | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
값 | e | x | c | e | s | s | i | v | 25856 | |
16진수 | 65 | 78 | 63 | 65 | 73 | 73 | 69 | 76 | 65 | 00 |
버퍼 오버플로 취약점을 악용하는 기술은 아키텍처, 운영 체제, 그리고 메모리 영역에 따라 다르다. 예를 들어, 힙(동적으로 할당된 메모리에 사용됨)에서의 악용은 콜 스택에서의 악용과 뚜렷하게 다릅니다. 일반적으로 힙 악용은 대상 시스템에서 사용되는 힙 관리자에 따라 다르며, 스택 악용은 아키텍처 및 컴파일러에서 사용되는 호출 규칙에 따라 달라집니다.
B의 값은 의도치 않게 문자열의 일부로 형성된 숫자로 대체되었다. 이 예에서 "e" 다음에 0 바이트가 오면 25856이 된다.
할당된 메모리의 끝을 지나 데이터를 쓰는 것은 때때로 운영 체제에 의해 감지되어 프로세스를 종료하는 세그먼테이션 오류 오류를 생성할 수 있다.
이러한 버퍼 오버플로를 방지하기 위해, `strcpy` 대신 `strlcpy` 함수를 사용하여 최대 용량을 제한할 수 있다.
```text
strlcpy(A, "excessive", sizeof(A));
```
`strlcpy`는 `strncpy`보다 안전하지만, 소스 문자열의 길이가 버퍼 크기보다 크거나 같은 경우 대상 버퍼를 널로 종료하지 않을 수 있으므로 주의해야 한다.
3. 이용
3. 1. 스택 기반 이용
기술적 성향을 가진 악의적 사용자가 스택 기반 버퍼 오버플로를 이용해 프로그램을 조작하는 방법은 다음과 같다:
트램폴라이닝이라는 방법을 사용하면, 사용자가 지정한 데이터의 주소를 모르더라도, 레지스터에 그 주소가 저장되어 있다면, 실행 흐름을 사용자 제공 데이터로 도약하도록 하는 어떤 실행코드의 주소로 귀환 주소를 덮어쓸 수 있다. 위치가 레지스터 R에 저장되어 있다면 jump R이나 call R과 같이 R에 저장되어 있는 주소로 건너뛰는 작동코드 opcode로 사용자 제공 데이터가 실행되도록 할 수 있다. 적당한 작동코드 또는 바이트의 위치는 DLL이나 실행 파일 자체 안에서 찾을 수 있다. 그러나 작동 코드의 주소는 전형적으로는 어떠한 무효 문자도 포함할 수 없고, 이러한 작동 코드의 위치는 응용 프로그램마다, 운영 체제 버전에 따라 달라질 수 있다. 메타스플로잇 프로젝트는 그러한 적당한 작동코드의 데이터베이스로 윈도우 운영 체제에 관한 것이 나열되어 있다.
스택 기반 버퍼 오버플로는 "덮어쓰기될 버퍼가 스택(즉, 지역 변수 또는 드물게 함수의 매개변수)에 할당"될 수 있는 경우 발생하는 취약점이다.[42] 이러한 취약점은 퍼징을 사용하여 발견되는 경우가 많다.[49]
C 언어나 C++로 작성된 프로그램의 경우, 사용자 공간을 더 세분화한다. 이들 언어로 작성된 프로그램에서는 가상 주소의 최저위부터 순서대로 프로그램의 실행 코드를 배치하는 '''코드 영역 (텍스트 영역이라고도 함)''', 초기화된 정적 변수, 전역 변수를 배치하는 '''데이터 영역''', 초기화되지 않은 정적 변수, 전역 변수를 배치하는 '''bss 영역'''을 확보한다.
한편, 사용자 공간의 가상 주소 최고위는 함수의 호출 스택을 저장하는 (가변 크기의) '''스택 영역'''으로 사용된다.
최저위 | … | 최고위 | |||
코드 영역 | 정적 영역 | 힙 영역 | … | 스택 영역 | |
데이터 영역 | bss 영역 |
스택 영역은 프로세스 내에서 호출되는 함수의 호출 스택을 저장하는 영역으로, 호출 스택 내의 각 함수 데이터 ('''스택 프레임'''이라고 함)를 나란히 저장한다.프로세스 내에서 함수 f가 함수 g를 호출한 경우, 호출 스택은 다음과 같다:
최저 주소 | … | 최고 주소 | ||||||
… | g의 스택 프레임 | f의 스택 프레임 | … | |||||
… | g의 처리에 필요한 임시 정보 | g의 지역 변수 | g의 SFP | g의 리턴 주소 | g의 인자 값 | f의 처리에 필요한 임시 정보 | … | … |
프로세스에서 현재 실행 중인 함수의 스택 프레임 위치를 기억하기 위해 프로세서에서 사용되는 것이 '''프레임 포인터'''이며, 구체적으로는 (현재 실행 중인 함수가 g라면) g의 SFP의 주소를 가리킨다. '''SFP'''는 함수 호출 시 호출하는 측 함수 프레임 포인터의 주소를 기억하기 위한 영역으로, f가 g를 호출했을 때, 스택 프레임 값 (=f의 SFP의 주소)을 g의 SFP에 저장한다. SFP는 "Saved Frame Pointer"의 약자로, 한국어 번역은 "'''저장된 프레임 포인터'''"이다.
한편 g의 '''리턴 주소'''는 호출된 함수 f의 '''프로그램 카운터'''의 주소를 저장한다.
예를 들어, 함수 g가 사용자로부터 (ASCII 코드) 문자열을 입력받아 입력된 문자열을 배열 char A[10]에 저장한다고 가정한다. A의 크기는 10이므로, 함수 g의 프로그래머는 사용자로부터 받은 문자열을 A에 저장하기 전에, 해당 문자열이 실제로 10자 이하인지 확인하는 메커니즘을 g에 구현해야 한다. 이러한 확인 메커니즘 구현을 잊었을 경우, 악의적인 사용자 (이하, "공격자"라고 한다)에 의해 스택 기반 버퍼 오버플로 공격을 받을 위험이 있다.
구체적으로, 공격자는 다음과 같은 문자열을 g에 입력한다:
>('''셸 코드''')…('''셸 코드의 가상 주소''')
여기서 '''셸 코드'''란, 어떤 악의적인 짧은 프로그램으로, 예를 들어 공격자를 위해 백도어를 열거나 멀웨어를 다운로드하는 것이다.
이 문자열이 배열 A의 선두부터 순서대로 기록되면, '''A[i]의 주소는 i가 클수록 커지므로''', 공격자가 입력 문자열의 길이를 적절하게 선택하면, 주소 공간에는 다음과 같이 데이터가 기록되고, '''g의 리턴 주소가 셸 코드의 가상 주소로 덮어씌워진다''':
낮은 주소 | … | 높은 주소 | ||||||
… | g의 스택 프레임 | f의 스택 프레임 | … | |||||
… | g의 처리에 필요한 임시 정보 | g의 지역 변수 | g의 SFP | g의 리턴 주소 | g의 인자 값 | f의 처리에 필요한 임시 정보 | … | … |
(셸 코드)… | … | (셸 코드의 가상 주소) |
따라서 함수 g가 종료될 때, g의 리턴 주소 (의 위치에 덮어씌워진 셸 코드의 가상 주소)가 읽히므로, 프로그램 카운터는 셸 코드의 위치로 점프하여, 공격자의 의도대로 셸 코드가 실행된다.
아래의 예에서 "X"는 프로그램이 실행될 때 스택에 있게 되는 데이터이다. 프로그램은 그 다음 작은 양의 저장공간만 필요한 함수 "Y"를 호출한다. 그리고 "Y"는 그 다음 많은 버퍼를 필요로 하는 "Z"를 호출한다.
Z | Y | X | |||||||
: | / | / | / |
만약 함수 Z가 오버플로를 발생시키면, 그것은 함수 Y나 주 프로그램에 포함된 데이터를 덮어 쓸 수도 있다:
Z | Y | X | |||||||
. | . | . | . | . | . | . | . | / | / |
3. 1. 1. 스택 기반 이용 예
아래의 예에서 "X"는 프로그램이 실행될 때 스택에 있게 되는 데이터이다. 프로그램은 그 다음 작은 양의 저장공간만 필요한 함수 "Y"를 호출한다. 그리고 "Y"는 그 다음 많은 버퍼를 필요로 하는 "Z"를 호출한다.Z | Y | X | |||||||
: | / | / | / |
만약 함수 Z가 오버플로를 발생시키면, 그것은 함수 Y나 주 프로그램에 포함된 데이터를 덮어 쓸 수도 있다:
Z | Y | X | |||||||
. | . | . | . | . | . | . | . | / | / |
이것은 대부분의 시스템에서 현재의 프로세스가 호출되기 전 실행되고 있던 프로그램 부분의 위치인 반환값을 스택이 갖고 있기 때문에 특히나 심각하다. 함수가 끝날 때 임시 저장소는 스택에서 제거되고 실행은 반환 주소로 되돌아간다. 그런데 반환 주소를 버퍼 오버플로가 덮어쓰면 어떤 다른 위치를 가리킬 것이다. 처음 예제에서와 같은 버퍼 오퍼플로가 우연히 발생하는 경우 거의 틀림없이 쓸모없는 위치일 것이다. 어떠한 프로그램 명령어 위치가 아닌 이상, 프로세스는 망가질 것이다. 그리고 악의적인 공격자가 시스템 보안과 충돌할 수 있는 임의 위치로 반환 주소를 바꿀 수 있다.
스택 버퍼 오버플로우를 악용하여 프로그램을 조작할 수 있는 몇 가지 방법이 있다.
- 취약한 버퍼 근처에 위치한 지역 변수를 덮어써서 프로그램 동작을 변경한다.
- 스택 프레임의 반환 주소를 덮어써서 공격자가 선택한 코드(일반적으로 쉘코드라고 함)를 가리키도록 한다. 함수가 반환되면 실행은 공격자의 쉘코드에서 재개된다.
- 함수 포인터[1] 또는 예외 처리기를 덮어써서 쉘코드를 가리키게 한 다음 쉘코드를 실행한다.
- 나중에 해당 프레임을 소유한 함수에서 사용될 다른 스택 프레임의 지역 변수(또는 포인터)를 덮어쓴다.[2]
공격자는 이러한 익스플로잇 중 하나를 일으키도록 데이터를 설계한 다음 취약한 코드가 사용자에게 제공하는 버퍼에 이 데이터를 배치한다. 스택 버퍼 오버플로우에 영향을 주기 위해 사용된 사용자 제공 데이터의 주소를 예측할 수 없는 경우, 원격 코드 실행을 일으키기 위해 스택 버퍼 오버플로우를 악용하는 것은 훨씬 더 어려워진다. 이러한 버퍼 오버플로우를 악용하는 데 사용할 수 있는 한 가지 기술은 "트램폴린"이라고 한다. 여기서 공격자는 취약한 스택 버퍼에 대한 포인터를 찾고 해당 포인터에 상대적인 쉘코드의 위치를 계산한다. 그런 다음 공격자는 덮어쓰기를 사용하여 메모리에 이미 있는 명령어로 점프하여 이번에는 포인터에 상대적인 두 번째 점프를 수행한다. 두 번째 점프는 쉘코드 내에서 실행 분기를 한다. 적절한 명령어는 대규모 코드에 종종 존재한다. 예를 들어 Metasploit Project는 적절한 opcode 데이터베이스를 유지 관리하지만 윈도우 운영 체제에서 발견된 opcode만 나열한다.[3]
C 언어나 C++로 작성된 프로그램의 경우, 사용자 공간을 더 세분화한다. 이들 언어로 작성된 프로그램에서는 가상 주소의 최저위부터 순서대로 프로그램의 실행 코드를 배치하는 '''코드 영역 (텍스트 영역이라고도 함)''', 초기화된 정적 변수, 전역 변수를 배치하는 '''데이터 영역''', 초기화되지 않은 정적 변수, 전역 변수를 배치하는 '''bss 영역'''을 확보한다.
한편, 사용자 공간의 가상 주소 최고위는 함수의 호출 스택을 저장하는 (가변 크기의) '''스택 영역'''으로 사용된다.
최저위 | … | 최고위 | |||
코드 영역 | 정적 영역 | 힙 영역 | … | 스택 영역 | |
데이터 영역 | bss 영역 |
스택 영역은 프로세스 내에서 호출되는 함수의 호출 스택을 저장하는 영역으로, 호출 스택 내의 각 함수 데이터 ('''스택 프레임'''이라고 함)를 나란히 저장한다.프로세스 내에서 함수 f가 함수 g를 호출한 경우, 호출 스택은 다음과 같다:
최저 주소 | … | 최고 주소 | ||||||
… | g의 스택 프레임 | f의 스택 프레임 | … | |||||
… | g의 처리에 필요한 임시 정보 | g의 지역 변수 | g의 SFP | g의 리턴 주소 | g의 인자 값 | f의 처리에 필요한 임시 정보 | … | … |
프로세스에서 현재 실행 중인 함수의 스택 프레임 위치를 기억하기 위해 프로세서에서 사용되는 것이 '''프레임 포인터'''이며, 구체적으로는 (현재 실행 중인 함수가 g라면) g의 SFP의 주소를 가리킨다. '''SFP'''는 함수 호출 시 호출하는 측 함수 프레임 포인터의 주소를 기억하기 위한 영역으로, f가 g를 호출했을 때, 스택 프레임 값 (=f의 SFP의 주소)을 g의 SFP에 저장한다. SFP는 "Saved Frame Pointer"의 약자로, 한국어 번역은 "'''저장된 프레임 포인터'''"이다.
한편 g의 '''리턴 주소'''는 호출된 함수 f의 '''프로그램 카운터'''의 주소를 저장한다。
예를 들어, 함수 g가 사용자로부터 (ASCII 코드) 문자열을 입력받아 입력된 문자열을 배열 char A[10]에 저장한다고 가정한다. A의 크기는 10이므로, 함수 g의 프로그래머는 사용자로부터 받은 문자열을 A에 저장하기 전에, 해당 문자열이 실제로 10자 이하인지 확인하는 메커니즘을 g에 구현해야 한다. 이러한 확인 메커니즘 구현을 잊었을 경우, 악의적인 사용자 (이하, "공격자"라고 한다)에 의해 스택 기반 버퍼 오버플로 공격을 받을 위험이 있다.
구체적으로, 공격자는 다음과 같은 문자열을 g에 입력한다:
('''셸 코드''')…('''셸 코드의 가상 주소''')여기서 '''셸 코드'''란, 어떤 악의적인 짧은 프로그램으로, 예를 들어 공격자를 위해 백도어를 열거나 멀웨어를 다운로드하는 것이다.
이 문자열이 배열 A의 선두부터 순서대로 기록되면, '''A[i]의 주소는 i가 클수록 커지므로''', 공격자가 입력 문자열의 길이를 적절하게 선택하면, 주소 공간에는 다음과 같이 데이터가 기록되고, '''g의 리턴 주소가 셸 코드의 가상 주소로 덮어씌워진다''':
낮은 주소 | … | 높은 주소 | ||||||
… | g의 스택 프레임 | f의 스택 프레임 | … | |||||
… | g의 처리에 필요한 임시 정보 | g의 지역 변수 | g의 SFP | g의 리턴 주소 | g의 인자 값 | f의 처리에 필요한 임시 정보 | … | … |
(셸 코드)… | … | (셸 코드의 가상 주소) |
따라서 함수 g가 종료될 때, g의 리턴 주소 (의 위치에 덮어씌워진 셸 코드의 가상 주소)가 읽히므로, 프로그램 카운터는 셸 코드의 위치로 점프하여, 공격자의 의도대로 셸 코드가 실행된다.
3. 2. 힙 기반 이용
힙 데이터 영역에서 일어나는 버퍼 오버플로를 힙 오버플로라 부르며 스택 기반 오버플로와는 다른 방법으로 이용할 수 있다.[4] 힙 상의 메모리는 동적으로 응용 프로그램에 의해 실행시간 중에 할당되고 보통 프로그램 데이터를 보관하고 있다. 이용하는 방법은 이 데이터를 특정한 방법으로 오염시켜 응용 프로그램이 연결 리스트 포인터(linked list pointer)등과 같은 내부 자료 구조를 덮어쓰게 한다.[4] 기본적인 힙 오버플로 기술은 동적 메모리 할당 연결(malloc 상위 수준 데이터)을 덮어씀으로써 프로그램 함수 포인터를 조작한다.[4] 마이크로소프트 GDI+가 JPEG를 처리하는 과정에서 발생하는 취약점은 힙 오버플로우가 제시할 수 있는 위험의 한 예이다.[4]malloc 등에서 히프 영역에 동적으로 메모리를 할당하는 함수에 대한 오버플로 공격이다.[43] 기본적인 공격 수법으로는, 함수가 히프에 할당한 메모리 영역이 2개 있을 때, 그 중 한쪽 영역에 대해 할당된 메모리 크기보다 큰 데이터를 입력하여 오버플로를 일으켜, 다른 쪽 메모리 영역을 덮어쓰는 것이다.
더욱 고도화된 힙 기반 버퍼 오버플로우 공격으로, malloc이 메모리 관리에 사용하는 메타데이터를 변조하는 기법이 있다. 예를 들어, 윈도우 XP SP1 또는 그 이전 버전의 윈도우에서는, malloc으로 할당된 메모리 영역을 오버플로우시켜, 해당 메모리 영역의 (가상 주소 공간에서) 인접한 사용되지 않은 chunk의 flink 및 blink를 임의의 주소로 덮어쓰는 공격이 가능했다. flink 및 blink는 coalesce 시점에 malloc에 의해 참조되므로 공격이 성공한다.
3. 3. 이용당하는 것을 막는 장벽
버퍼가 읽히거나 실행되기 전에 버퍼를 조작하면 이용하려는 시도를 막을 수도 있다.[5] 조작에는 대문자로 또는 소문자로 변환, 제어 부호 제거, 숫자 영문자 제거 등이 포함된다.[5] 이러한 방지 수단을 회피하는 기술도 존재한다: 숫자 영문자 코드, 다형성 코드, 자체 수정 코드, libc 귀환 공격 등.[5] 같은 방법을 침입 감지 시스템에 의한 피탐지를 회피하는 데도 사용할 수 있다.[5] 어떤 경우에는, 유니코드 변환의 경우도 포함되지만, 서비스 거부만 가능한 것으로 취약점의 위협이 잘못 표현되었지만 사실은 임의 코드의 원격 실행이 가능할 수도 있다.[5]3. 4. 이용의 실제
실제 공격에서는 주소의 무효값 바이트, 셸 코드 위치 가변성, 환경 간 상이성, 대응 수단 등 다양한 요소를 고려해야 한다.실제 익스플로잇에서는 익스플로잇이 안정적으로 작동하기 위해 극복해야 할 다양한 과제가 있다.
함수 g에 삽입할 공격용 문자열은 "NOP 스레드 + 셸 코드 + 반환 주소 반복" 형식을 가지며, g의 반환 주소가 "반환 주소 반복" 부분에 도달하지 않으면 공격이 성공하지 않는다. 따라서 함수 g에 공격용 문자열을 삽입하는 위치와 g의 반환 주소가 (가상 주소 공간에서) 너무 가까우면 공격에 필요한 길이의 셸 코드를 삽입할 수 없는 문제가 공격자에게 발생한다.
그러나 공격자가 공격 대상 머신의 환경 변수를 설정할 수 있는 상황에서는 공격자는 이 문제를 회피한 공격이 가능하다. 대상 머신에서 프로세스가 실행될 때, 해당 프로세스의 가상 주소 공간에 환경 변수가 로드되므로, 공격자가 사전에 대상 머신의 환경 변수에 "NOP 스레드 + 셸 코드"를 써 넣으면 프로세스의 가상 주소에 "NOP 스레드 + 셸 코드"가 생성된다. 프로세스 내에서 함수 g가 실행될 때, 공격자는 공격용 문자열을 g에 입력하고 반환 주소를 해당 NOP 스레드로 덮어쓰면 공격이 성공하게 된다.
더욱 확실한 공격 방법으로, 공격 프로그램 h 내에 환경 변수를 읽는 함수(getenv() 등)를 사용하는 방법도 있다.
3. 4. 1. NOP 슬라이드 기법
NOP 슬라이드 기법은 가장 오래되고 가장 널리 알려진 스택 버퍼 오버플로 공격 기법이다. 이 기법은 NOP (No Operation) 기계어 코드를 이용하여 표적 영역의 크기를 효과적으로 늘려 공격 성공률을 높인다. 공격자는 스택의 더 큰 영역을 NOP 명령어로 채우고, 공격 데이터 끝에 셸코드가 있는 버퍼의 최상위 위치로 상대 점프하는 명령어를 둔다. 이 NOP 명령어의 모음을 "NOP 슬라이드"라고 부르는데, 덮어 쓴 반환 주소가 NOP 영역 내의 주소값을 가리키면 NOP 명령어들을 따라 "미끄러져" 최종적으로 셸 코드에 도달하기 때문이다. 이 기법을 사용하면 공격자는 상대적으로 작은 크기의 셸 코드 위치 대신 스택에서 NOP 슬라이드의 위치를 추정하면 된다.NOP 슬라이드는 자주 사용되므로, 많은 침입 방지 시스템에서 NOP 기계어 명령어 패턴을 탐색하여 셸코드 사용을 탐지한다.[50] NOP 슬라이드는 반드시 전통적인 NOP 기계어 코드만 포함하는 것은 아니며, 셸코드 실행에 영향을 주지 않는 범위 내에서 어떤 명령어도 하드웨어 보조 NOP 자리에 사용될 수 있다. 따라서 공격자는 NOP 슬라이드를 셸코드 실행에 지장이 없는 임의로 선택한 명령어로 채우는 것이 일반적이다.
이 방법은 공격 성공 확률을 크게 높이지만, 몇 가지 문제점이 있다.
- 이 기법을 이용한 공격은 스택 상의 거리 오프셋(offset)을 추정하여 NOP 슬라이드 영역에 들어가게 해야 하므로 어느 정도 운에 의존한다. 잘못 추정하면 대상 프로그램이 깨지고 시스템 관리자에게 공격을 알릴 수 있다.
- 효과적인 NOP 슬라이드를 만들기 위해서는 할당받을 수 있는 메모리에 비해 너무 커야 할 수 있다. 할당된 버퍼 크기가 작고 현재 스택의 깊이가 얕을 때 문제가 될 수 있다.
이러한 문제점에도 불구하고, NOP 슬라이드는 주어진 플랫폼, 환경, 상황에 따라 유일하게 작동하는 방법일 수 있으므로 여전히 중요한 기법이다.
3. 4. 2. 레지스터에 저장된 주소로 건너뛰기
레지스터로 건너뛰기(jump to register) 기법은 NOP 슬라이드와 같은 추가 공간이나 스택 위치 오프셋을 추정하지 않아도 잘 작동한다. 이 전략은 복귀 포인터를 덮어써서 통제된 버퍼, 즉 셸코드를 담고 있는 레지스터의 위치로 프로그램이 건너뛰게 만드는 것이다. 예를 들어 레지스터 A가 어떤 버퍼의 시작점을 담고 있다면, 이 레지스터를 오퍼랜드(operand)로 삼은 어떠한 건너뛰기 또는 호출로 실행 흐름의 제어권을 얻을 수 있다.[79] 실전에서는 어떤 프로그램이 의도적으로는 어떤 특정 레지스터에 저장된 주소로 건너뛰는 명령을 포함하지 않을 수도 있다. 전통적인 해법은 어떤 의도되지 않은 적당한 작동코드 opcode 사례를 프로그램 메모리 어딘가 고정된 위치에서 찾는 것이다.
예를 들면, 뜻하지 않게 i386 JMP esp 명령이 포함된 경우가 ntdll.dll 안의 DbgPrint()함수에 있었다. 이 명령의 작동 코드는 FF E4이다. 이 연속 두 바이트 패턴이 call DbgPrint 명령에서 1Byte 떨어진 곳 주소 0x7C941EED에 나타났다. 공격자가 프로그램의 귀환 주소를 이 주소로 덮어 쓰면, 프로그램이 처음에는 0x7C941EED로 건너뛰어 실행 코드 FF E4를 JMP esp 명령으로 인식하여 스택의 정상 위치로 jump, 공격자의 코드를 실행할 것이다.
이 기법이 가능하다면 취약성의 심각성이 상당히 증가한다. 이는 왜냐하면 이용이 충분히 신뢰성 있게 작동하여 공격을 자동화, 실행하면 가상적으로 성공을 보증할 수 있게 되기 때문이다. 이러한 이유로, 이 기법이 인터넷 웜에서 가장 흔히 버퍼 오버플로 취약점을 이용하기 위해 사용된다.
이 방법은 또한 셸코드의 위치를 윈도 플랫폼 상에서 덮어씌어진 귀환 주소 뒤에 위치하더라고 가능하다. 실행 코드는 주소 0x00400000에 기반하고, x86은 리틀 엔디안 구조이므로 귀환 주소의 최후 바이트는 반드시 무효값이 되고, 이것이 버퍼 복사를 마감하며 그 이후에는 아무것도 씌지 않는다. 이로 하여 셸코드의 크기는 버퍼의 크기로 제한되며, 지나친 규제가 될 수 있다. DLL은 고위 메모리 (0x01000000 이상)에 저장되며 따라서 무효 바이트가 없는 주소를 가지므로, 이 방법은 무효 바이트 (또는 다른 허용 안되는 문자)를 덮어씌어진 복귀 주소에서 제거할 수 있다. 이런 식으로 사용되면, 이 방법은 때때로 DLL 프램폴라이닝이라고 불린다.
공격자가 입력한 데이터(익스플로잇 등)의 주소를 정확히는 알수 없으나 해당 주소가 레지스터에 저장되어 있다는 것을 알고 있는 경우에는 ''트램폴리닝''(trampolining)이라고 불리는 기법이 사용된다. 이 기법에서는 공격자가 입력한 데이터로 점프하는 연산 코드의 주소를 반환 주소에 덮어쓴다. 예를 들어 주소가 레지스터 R에 저장되어 있는 경우, jump R(혹은 call R 등)이라는 연산 코드가 저장된 주소로 점프하게 하여 사용자가 입력한 데이터를 실행시킨다. 이 기법에서 사용하는 연산 코드는 DLL나 실행 파일 내의 것을 이용한다. 단, 일반적으로 연산 코드의 주소에 널 문자가 포함되어 있어서는 안 되며, 처리에 사용하는 연산 코드의 주소는 애플리케이션이나 운영 체제의 버전에 따라 다르다. 메타스플로잇 프로젝트는 이러한 목적에 적합한 연산 코드 데이터베이스 중 하나이며, Windows에서 사용할 수 있는 연산 코드가 기재되어 있다[51]。
Return-to-Register 공격은 "ret 명령 실행 후 레지스터가 가리키는 주소에 부정한 명령 코드를 삽입하고, 그 위에 '해당 레지스터 값으로 실행을 옮기는 명령군이 저장된 주소'로 리턴 주소를 덮어쓰는 공격"을 말한다.
ret2esp (Return to esp) 공격은 2017년 현재 ASLR(주소 공간 배치 난수화) 구현의 기본 설정에서는 코드 영역(.text 섹션)을 난수화하지 않는 점을 이용하여 ASLR을 우회하는 공격 기법이다.[80] 여기서 esp는 x86에서 스택 포인터이다. 이 공격은 .text 섹션 내에 "jmp esp"와 같은 명령어가 있는 경우 성립한다.[80] 공격자는 버퍼 오버플로우를 이용하여 esp의 (가상 주소 공간상의) 아래에 셸 코드를 배치한 후, 반환 주소를 "jmp esp" 위치로 변경한다. 그러면 먼저 반환 주소의 변경에 의해 "jmp esp" 위치로 점프하고, 다음에 "jmp esp"가 실행되어 esp 위치로 점프하므로, 그 아래에 배치된 셸 코드가 실행된다.[80]
4. 보호 대응 수단
C/C++는 메모리 접근에 대한 내장 보호 기능이 없어 버퍼 오버플로에 취약하다.[6] C와 C++는 메모리상 어떤 부분에서도 데이터 접근과 덮어쓰기에 대한 내장 보호 기능이 없다.[6] 더 구체적으로, 데이터가 어떤 배열(버퍼의 구현)에 씌어지는 데이터가 그 배열의 범위 안에 씌어지는지 검사하지 않는다. 그러나 표준 C++ 라이브러리는 데이터를 안전히 저장하는 많은 방법을 제공하고, C언어에서 버퍼 오버플로를 회피하기 위한 기술도 존재한다.[6] 예를 들어, C++의 표준 템플릿 라이브러리(STL)는 프로그래머가 데이터 접근 시 명시적으로 경계 검사를 호출하는 경우 선택적으로 경계 검사를 수행할 수 있는 컨테이너를 제공한다. `vector`의 멤버 함수 `at()`는 경계 검사를 수행하고 경계 검사에 실패하면 `out_of_range` 예외를 발생시킨다.[7]
어셈블리, C, C++는 메모리에 직접 접근을 허용하고 강력한 형식 지정을 지원하지 않기 때문에 버퍼 오버플로에 취약한 인기 프로그래밍 언어이다.[6]
반면, COBOL, Java, Eiffel, Python 등과 같이 강력한 형식 지정을 지원하고 메모리에 직접 접근을 허용하지 않는 언어는 대부분의 경우 버퍼 오버플로를 방지한다.[6] C 또는 C++ 이외의 많은 프로그래밍 언어는 런타임 검사를 제공하며, 경우에 따라 경고를 보내거나 예외를 발생시킬 수 있는 컴파일 시간 검사도 제공한다. 이러한 언어의 예로는 에이다, Eiffel, 리스프, 모듈라-2, 스몰토크, OCaml 및 사이클론, Rust, D와 같은 C 파생 언어가 있다.[6] 자바 및 .NET Framework 바이트코드 환경 또한 모든 배열에 대한 경계 검사를 요구한다. 거의 모든 인터프리터 언어는 버퍼 오버플로를 방지하여 잘 정의된 오류 조건을 신호로 보낸다.
소프트웨어 엔지니어는 어떤 언어와 컴파일러 설정을 사용할지 결정할 때 안전성과 성능 비용 간의 트레이드 오프를 신중하게 고려해야 한다.
버퍼 오버플로는 C와 C++ 언어에서 흔히 발생하는데, 이는 이러한 언어가 데이터 형을 보관하는 버퍼의 저수준 표현 상세를 노출시키기 때문이다. 따라서 버퍼 오버플로를 방지하기 위해서는 버퍼 관리 코드 내에서 높은 수준의 정확성을 유지해야 한다. 오랫동안 `gets`, `scanf`, `strcpy` 등 경계 검사를 하지 않는 표준 라이브러리 함수 사용을 피하는 것이 권장되어 왔다.[8] 모리스 웜은 fingerd 내에서 호출되는 `gets` 함수를 이용했다.[8]
경계 검사를 포함한 버퍼 관리를 중앙 집중화하고 자동적으로 실시하는 잘 작성되고 검사된 추상 데이터형 라이브러리는 버퍼 오버플로 발생과 충격을 줄일 수 있다. 버퍼 오버플로가 자주 발생하는 언어의 두 가지 주요한 기본 데이터 형은 문자열과 배열이다. 따라서 이러한 데이터 형에서 버퍼 오버플로를 방지하는 라이브러리는 필요한 영역의 대부분을 담당해 줄 수 있다. 그러나 이러한 안전한 라이브러리를 사용하지 못하면 버퍼 오버플로 및 다른 취약점을 낳을 수 있으며, 라이브러리 자체의 버그 역시 잠재적인 취약점이 된다. "안전한" 라이브러리 구현에는 "The Better String Library",[9] Vstr,[10] Erwin[11] 등이 있다. OpenBSD 운영 체제의 C 라이브러리는 strlcpy와 strlcat 함수를 제공하지만, 이는 완전한 안전 라이브러리 구현보다는 제한적이다.
2006년 9월 C 언어 표준 위원회 기술 보고서 24731이 공개되었는데,[12] 이는 표준 C 라이브러리의 문자열과 입출력 함수에 기반하여 버퍼 크기 매개 변수를 추가로 가진 함수들을 명세하였다. 그러나 이러한 함수들의 버퍼 오버플로 방지 효력에 대해서는 논란의 여지가 있다. 프로그래머가 함수 호출마다 개입해야 하는데, 이는 더 오래된 표준 라이브러리 함수의 버퍼 오버플로를 방지하게 만드는 것과 같은 수준의 일이다.[13]
더 안전한 기능을 제공하는 함수를 사용하는 것도 한 방법이다. 예를 들어, 문자열 길이를 구하는 C 함수인 `strlen`, `wcslen` 함수는 NULL 문자를 만날 때까지 다음 메모리를 참조하는 방식으로 작동하기 때문에, 원래 문자열 끝에 NULL이 없으면 메모리 영역 끝을 넘어설 수 있다. 이때 개선된 함수인 `strlen s`나 `wcslen s`를 사용하면 참조할 최대 메모리를 제한할 수 있다. 이 외에도 기존 문자열 함수 뒤에 _s가 붙은 이름의 함수를 사용하면 버퍼 오버플로 문제를 개선할 수 있다.[87]
표준 C 라이브러리를 사용하는 대신, 버퍼 오버플로를 사전에 방지하거나 오류로 감지하는 안전한 라이브러리를 사용하는 것도 중요하다. 이러한 라이브러리로는 Managed String(리눅스 환경), ISO/IEC 9899:2011 Annex K(윈도우 환경), SafeStr(리눅스 환경, 윈도우 환경), "The Better String Library", Vstr, Erwin 등이 있다. 또한 BSD libc 등, C 라이브러리의 구현에 따라 strlcpy나 strlcat와 같은, 더 안전에 배려한 문자열용 함수가 준비되어 있다.
버퍼 오버플로 보호는 가장 일반적인 버퍼 오버플로를 검출하기 위해 사용되며 함수가 귀환할 때 스택이 변경되었는지 검사한다. 변경되었다면 프로그램이 세그멘테이션 오류를 발생 시키며 중단된다.[14] 그러한 시스템의 세가지 예가 gcc 패치인 립세이프(LibSafe),[15] 스택 가드,[16] 프로폴리스이다. 마이크로소프트의 데이터 실행 방지 모드는 명백히 SEH 예외 처리기를 가리키는 포인터를 덮어쓰기로부터 보호한다.[17] 더 강력한 스택 보호는 스택을 데이터용과 함수 귀환용으로 나누는 것이다. 이러한 구분은 포스 프로그래밍 언어에 채택되어 있지만, 이는 보안을 위한 것은 아니었다. 어쨌든, 이는 귀환 주소가 아닌 민감한 데이터는 여전히 덮어씌어질 가능성이 있어 완벽한 해결책은 아니다.
이러한 유형의 보호는 모든 공격을 감지하지 못하므로 완전히 정확하지 않다. StackGuard와 같은 시스템은 공격의 동작에 더 중점을 두고 있어 범위 검사 시스템에 비해 효율적이고 빠르다.[18]
컴파일 시 '''카나리아'''(canary)[56][57] 또는 '''쿠키'''라고 불리는 영역을 삽입하여, 프로그램 실행 중 카나리아를 계속 감시하여 버퍼 오버플로 발생시 프로그램을 중단 하는 기법이 있다.
- 스택가드[58]
- 마이크로소프트 비주얼 C++의 GS 옵션[59]
버퍼 오버플로는 저장된 주소를 포함한 포인터를 조작하여 작동한다. PointGuard는 공격자가 포인터와 주소를 신뢰성 있게 조작하는 것을 방지하기 위해 컴파일러 확장 기능으로 제안되었다.[19] 이 접근 방식은 컴파일러가 포인터를 사용 전후에 자동으로 XOR 인코딩하는 코드를 추가하여 작동한다. 이론적으로 공격자는 포인터를 인코딩하고 디코딩하는 데 어떤 값이 사용될지 알 수 없으므로, 새로운 값으로 덮어쓸 경우 포인터가 무엇을 가리킬지 예측할 수 없다. PointGuard는 출시되지 않았지만, 윈도우 XP SP2 및 윈도우 서버 2003 SP1부터 마이크로소프트는 이와 유사한 접근 방식을 구현했다.[20] 마이크로소프트는 포인터 보호 기능을 자동 기능으로 구현하는 대신 호출할 수 있는 API 루틴을 추가했다.
XOR은 선형이기 때문에 공격자는 주소의 하위 바이트만 덮어써서 인코딩된 포인터를 조작할 수 있다. 공격자가 익스플로잇을 여러 번 시도하거나 여러 위치 중 하나(예: NOP 슬레드 내의 모든 위치)를 가리키도록 포인터를 만들어 공격을 완료할 수 있는 경우 이러한 조작이 가능하다.[21] 마이크로소프트는 부분적인 덮어쓰기에 대한 이 취약점을 해결하기 위해 인코딩 방식에 임의의 회전을 추가했다.[22]
실행 공간 보호는 버퍼 오버플로를 방지하기 위한 방법으로, 스택이나 힙 상의 코드가 실행되는 것을 막는다. 공격자는 버퍼 오버플로를 이용하여 임의의 코드를 프로그램의 메모리에 삽입할 수 있지만, 실행 영역 보호가 있다면, 그 코드를 실행하고자 하는 어떠한 시도도 예외를 발생시킨다.[23][24][25][26][27][28]
어떤 CPU는 NX (No eXecute) 또는 XD (eXecute Disabled) 비트라는 기능을 지원하는데, 소프트웨어와 연계하여 데이터의 페이지 (즉 스택이나 힙을 담고 있는)를 읽고 쓰기는 가능하나 실행 불가로 표시할 수 있다. 오픈 BSD, OS X 같은 유닉스 운영 체제는 실행 가능한 공간 보호와 함께 출시되었다.[23][24][25] (예: W^X)
새로운 마이크로소프트 윈도우 변종도 실행 공간 보호를 지원하며 데이터 실행 방지라고 부른다.[26]
실행 공간 보호로 libc로 귀환 공격 또는 공격자 코드 실행에 의지하지 않는 공격을 방지한다고 보증할 수는 없다. 하지만, 64비트 시스템에서 주소 공간 배치 난수화(ASLR)를 사용하는 경우, 실행 공간 보호는 이러한 공격을 실행하기 더욱 어렵게 만든다.
데이터를 저장해야 할 영역에서는 실행을 불가능하게 하는 '''W^X''' ('''WX''' 또는 '''W^X'''로 약칭)라는 대책 기법이 알려져 있다. WX의 Windows에서의 구현은 '''DEP''' (data execution prevention영어, 데이터 실행 방지)라고 불린다. 또한 DEP에서는 SEH 예외 핸들러에 대한 포인터가 덮어 쓰이지 않도록 명시적으로 보호를 수행한다.
2018년 현재 널리 사용되는 x64 아키텍처의 프로세서에서는 WX를 실현하기 위해 데이터 영역임을 식별하는 '''NX 비트'''라는 메커니즘이 하드웨어 수준에서 지원되고 있다. (인텔에서는 NX 비트를 XD 비트라고 부르지만 동일한 것이다.)
주소 공간 배치 난수화(Address space layout randomization, ASLR)는 컴퓨터 보안 기능으로 중요 데이터 영역, 예를 들어 실행 코드의 기반 주소, 라이브러리, 힙, 스택 주소 등을 임의로 프로세서의 주소 공간에 배치하는 것이다.[29] 함수와 변수를 찾을 수 있는 가상 메모리 주소의 난수화로 버퍼 오버플로 이용이 더 어려워지지만 불가능한 것은 아니다.[29] 공격자가 이용 시도를 각각의 시스템에 따라 다르게 하도록 강요하므로 인터넷 웜 방어에 더 유용하다.[29]
ASLR은 버퍼 오버플로 공격의 발전 형태인 Return-to-libc 공격을 완화할 수 있지만, Return-oriented programming에는 대응할 수 없다. 커널 공간의 ASLR을 '''KASLR''' (kernel address space layout randomization)이라고 하며, 리눅스 커널(버전 3.14 이후)[69], iOS(버전 6 이후)[70] 등에서 구현되어 있다.
gcc와 g++로 컴파일할 때 스택 영역과 힙 영역에 대해서는 ASLR을 사용하지만, 코드 영역에 ASLR을 사용하려면 옵션 "-pie"를 사용해야 한다.[71] 또한 공유 라이브러리에 ASLR을 사용하려면 옵션 "-fPIC"를 지정한다.[71]
심층 패킷 조사(DPI)는 네트워크 경계에서 공격 고유 신호와 휴리스틱을 사용하여 버퍼 오버플로를 악용하려는 매우 기본적인 원격 시도를 탐지한다. 이 기술은 알려진 공격의 시그니처를 가진 패킷을 차단할 수 있다. 이전에는 긴 일련의 No-Operation 명령(NOP-sled)이 감지되면 패킷을 막을 수 있었으며, 페이로드 위치가 약간 가변적인 상황에서 사용되었다.
패킷 스캔은 알려진 공격만 방지할 수 있고 NOP 슬라이드는 다양한 방법으로 암호화 할 수 있기 때문에 효과적이지 못한 방법이다. 공격자들은 경험론적 패킷 스캐너와 침입 감지 시스템의 탐지를 회피하기 위하여 영숫자 코드, 변형, 또는 자기 수정 방식의 셸코드를 사용하기 시작하였다.
4. 1. 프로그래밍 언어 선택
C/C++는 메모리 접근에 대한 내장 보호 기능이 없어 버퍼 오버플로에 취약하다.[6] C와 C++는 메모리상 어떤 부분에서도 데이터 접근과 덮어쓰기에 대한 내장 보호 기능이 없다.[6] 더 구체적으로, 데이터가 어떤 배열(버퍼의 구현)에 씌어지는 데이터가 그 배열의 범위 안에 씌어지는지 검사하지 않는다. 그러나 표준 C++ 라이브러리는 데이터를 안전히 저장하는 많은 방법을 제공하고, C언어에서 버퍼 오버플로를 회피하기 위한 기술도 존재한다.[6] 예를 들어, C++의 표준 템플릿 라이브러리(STL)는 프로그래머가 데이터 접근 시 명시적으로 경계 검사를 호출하는 경우 선택적으로 경계 검사를 수행할 수 있는 컨테이너를 제공한다. `vector`의 멤버 함수 `at()`는 경계 검사를 수행하고 경계 검사에 실패하면 `out_of_range` 예외를 발생시킨다.[7]어셈블리, C, C++는 메모리에 직접 접근을 허용하고 강력한 형식 지정을 지원하지 않기 때문에 버퍼 오버플로에 취약한 인기 프로그래밍 언어이다.[6]
반면, COBOL, Java, Eiffel, Python 등과 같이 강력한 형식 지정을 지원하고 메모리에 직접 접근을 허용하지 않는 언어는 대부분의 경우 버퍼 오버플로를 방지한다.[6] C 또는 C++ 이외의 많은 프로그래밍 언어는 런타임 검사를 제공하며, 경우에 따라 경고를 보내거나 예외를 발생시킬 수 있는 컴파일 시간 검사도 제공한다. 이러한 언어의 예로는 에이다, Eiffel, 리스프, 모듈라-2, 스몰토크, OCaml 및 사이클론, Rust, D와 같은 C 파생 언어가 있다.[6] 자바 및 .NET Framework 바이트코드 환경 또한 모든 배열에 대한 경계 검사를 요구한다. 거의 모든 인터프리터 언어는 버퍼 오버플로를 방지하여 잘 정의된 오류 조건을 신호로 보낸다.
소프트웨어 엔지니어는 어떤 언어와 컴파일러 설정을 사용할지 결정할 때 안전성과 성능 비용 간의 트레이드 오프를 신중하게 고려해야 한다.
4. 2. 안전한 라이브러리 사용
버퍼 오버플로는 C와 C++ 언어에서 흔히 발생하는데, 이는 이러한 언어가 데이터 형을 보관하는 버퍼의 저수준 표현 상세를 노출시키기 때문이다. 따라서 버퍼 오버플로를 방지하기 위해서는 버퍼 관리 코드 내에서 높은 수준의 정확성을 유지해야 한다. 오랫동안gets
, scanf
, strcpy
등 경계 검사를 하지 않는 표준 라이브러리 함수 사용을 피하는 것이 권장되어 왔다.[8] 모리스 웜은 fingerd 내에서 호출되는 gets
함수를 이용했다.[8]경계 검사를 포함한 버퍼 관리를 중앙 집중화하고 자동적으로 실시하는 잘 작성되고 검사된 추상 데이터형 라이브러리는 버퍼 오버플로 발생과 충격을 줄일 수 있다. 버퍼 오버플로가 자주 발생하는 언어의 두 가지 주요한 기본 데이터 형은 문자열과 배열이다. 따라서 이러한 데이터 형에서 버퍼 오버플로를 방지하는 라이브러리는 필요한 영역의 대부분을 담당해 줄 수 있다. 그러나 이러한 안전한 라이브러리를 사용하지 못하면 버퍼 오버플로 및 다른 취약점을 낳을 수 있으며, 라이브러리 자체의 버그 역시 잠재적인 취약점이 된다. "안전한" 라이브러리 구현에는 "The Better String Library",[9] Vstr,[10] Erwin[11] 등이 있다. OpenBSD 운영 체제의 C 라이브러리는 strlcpy와 strlcat 함수를 제공하지만, 이는 완전한 안전 라이브러리 구현보다는 제한적이다.
2006년 9월 C 언어 표준 위원회 기술 보고서 24731이 공개되었는데,[12] 이는 표준 C 라이브러리의 문자열과 입출력 함수에 기반하여 버퍼 크기 매개 변수를 추가로 가진 함수들을 명세하였다. 그러나 이러한 함수들의 버퍼 오버플로 방지 효력에 대해서는 논란의 여지가 있다. 프로그래머가 함수 호출마다 개입해야 하는데, 이는 더 오래된 표준 라이브러리 함수의 버퍼 오버플로를 방지하게 만드는 것과 같은 수준의 일이다.[13]
더 안전한 기능을 제공하는 함수를 사용하는 것도 한 방법이다. 예를 들어, 문자열 길이를 구하는 C 함수인
strlen
, wcslen
함수는 NULL 문자를 만날 때까지 다음 메모리를 참조하는 방식으로 작동하기 때문에, 원래 문자열 끝에 NULL이 없으면 메모리 영역 끝을 넘어설 수 있다. 이때 개선된 함수인 strlen s
나 wcslen s
를 사용하면 참조할 최대 메모리를 제한할 수 있다. 이 외에도 기존 문자열 함수 뒤에 _s가 붙은 이름의 함수를 사용하면 버퍼 오버플로 문제를 개선할 수 있다.[87]표준 C 라이브러리를 사용하는 대신, 버퍼 오버플로를 사전에 방지하거나 오류로 감지하는 안전한 라이브러리를 사용하는 것도 중요하다. 이러한 라이브러리로는 Managed String(리눅스 환경), ISO/IEC 9899:2011 Annex K(윈도우 환경), SafeStr(리눅스 환경, 윈도우 환경), "The Better String Library", Vstr, Erwin 등이 있다. 또한 BSD libc 등, C 라이브러리의 구현에 따라 strlcpy나 strlcat와 같은, 더 안전에 배려한 문자열용 함수가 준비되어 있다.
4. 3. 버퍼 오버플로 보호
버퍼 오버플로 보호는 가장 일반적인 버퍼 오버플로를 검출하기 위해 사용되며 함수가 귀환할 때 스택이 변경되었는지 검사한다. 변경되었다면 프로그램이 세그멘테이션 오류를 발생 시키며 중단된다.[14] 그러한 시스템의 세가지 예가 gcc 패치인 립세이프(LibSafe),[15] 스택 가드,[16] 프로폴리스이다. 마이크로소프트의 데이터 실행 방지 모드는 명백히 SEH 예외 처리기를 가리키는 포인터를 덮어쓰기로부터 보호한다.[17] 더 강력한 스택 보호는 스택을 데이터용과 함수 귀환용으로 나누는 것이다. 이러한 구분은 포스 프로그래밍 언어에 채택되어 있지만, 이는 보안을 위한 것은 아니었다. 어쨌든, 이는 귀환 주소가 아닌 민감한 데이터는 여전히 덮어씌어질 가능성이 있어 완벽한 해결책은 아니다.이러한 유형의 보호는 모든 공격을 감지하지 못하므로 완전히 정확하지 않다. StackGuard와 같은 시스템은 공격의 동작에 더 중점을 두고 있어 범위 검사 시스템에 비해 효율적이고 빠르다.[18]
컴파일 시 '''카나리아'''(canary)[56][57] 또는 '''쿠키'''라고 불리는 영역을 삽입하여, 프로그램 실행 중 카나리아를 계속 감시하여 버퍼 오버플로 발생시 프로그램을 중단 하는 기법이 있다.
- 스택가드[58]
- 마이크로소프트 비주얼 C++의 GS 옵션[59]
4. 4. 포인터 보호
버퍼 오버플로는 저장된 주소를 포함한 포인터를 조작하여 작동한다. PointGuard는 공격자가 포인터와 주소를 신뢰성 있게 조작하는 것을 방지하기 위해 컴파일러 확장 기능으로 제안되었다.[19] 이 접근 방식은 컴파일러가 포인터를 사용 전후에 자동으로 XOR 인코딩하는 코드를 추가하여 작동한다. 이론적으로 공격자는 포인터를 인코딩하고 디코딩하는 데 어떤 값이 사용될지 알 수 없으므로, 새로운 값으로 덮어쓸 경우 포인터가 무엇을 가리킬지 예측할 수 없다. PointGuard는 출시되지 않았지만, 윈도우 XP SP2 및 윈도우 서버 2003 SP1부터 마이크로소프트는 이와 유사한 접근 방식을 구현했다.[20] 마이크로소프트는 포인터 보호 기능을 자동 기능으로 구현하는 대신 호출할 수 있는 API 루틴을 추가했다.XOR은 선형이기 때문에 공격자는 주소의 하위 바이트만 덮어써서 인코딩된 포인터를 조작할 수 있다. 공격자가 익스플로잇을 여러 번 시도하거나 여러 위치 중 하나(예: NOP 슬레드 내의 모든 위치)를 가리키도록 포인터를 만들어 공격을 완료할 수 있는 경우 이러한 조작이 가능하다.[21] 마이크로소프트는 부분적인 덮어쓰기에 대한 이 취약점을 해결하기 위해 인코딩 방식에 임의의 회전을 추가했다.[22]
4. 5. 실행 공간 보호
실행 공간 보호는 버퍼 오버플로를 방지하기 위한 방법으로, 스택이나 힙 상의 코드가 실행되는 것을 막는다. 공격자는 버퍼 오버플로를 이용하여 임의의 코드를 프로그램의 메모리에 삽입할 수 있지만, 실행 영역 보호가 있다면, 그 코드를 실행하고자 하는 어떠한 시도도 예외를 발생시킨다.[23][24][25][26][27][28]어떤 CPU는 NX (No eXecute) 또는 XD (eXecute Disabled) 비트라는 기능을 지원하는데, 소프트웨어와 연계하여 데이터의 페이지 (즉 스택이나 힙을 담고 있는)를 읽고 쓰기는 가능하나 실행 불가로 표시할 수 있다. 오픈 BSD, OS X 같은 유닉스 운영 체제는 실행 가능한 공간 보호와 함께 출시되었다.[23][24][25] (예: W^X)
새로운 마이크로소프트 윈도우 변종도 실행 공간 보호를 지원하며 데이터 실행 방지라고 부른다.[26]
실행 공간 보호로 libc로 귀환 공격 또는 공격자 코드 실행에 의지하지 않는 공격을 방지한다고 보증할 수는 없다. 하지만, 64비트 시스템에서 주소 공간 배치 난수화(ASLR)를 사용하는 경우, 실행 공간 보호는 이러한 공격을 실행하기 더욱 어렵게 만든다.
데이터를 저장해야 할 영역에서는 실행을 불가능하게 하는 '''W^X''' ('''WX''' 또는 '''W^X'''로 약칭)라는 대책 기법이 알려져 있다. WX의 Windows에서의 구현은 '''DEP''' (data execution prevention영어, 데이터 실행 방지)라고 불린다. 또한 DEP에서는 SEH 예외 핸들러에 대한 포인터가 덮어 쓰이지 않도록 명시적으로 보호를 수행한다.
2018년 현재 널리 사용되는 x64 아키텍처의 프로세서에서는 WX를 실현하기 위해 데이터 영역임을 식별하는 '''NX 비트'''라는 메커니즘이 하드웨어 수준에서 지원되고 있다. (인텔에서는 NX 비트를 XD 비트라고 부르지만 동일한 것이다.)
4. 6. 주소 공간 배치 난수화
주소 공간 배치 난수화(Address space layout randomization, ASLR)는 컴퓨터 보안 기능으로 중요 데이터 영역, 예를 들어 실행 코드의 기반 주소, 라이브러리, 힙, 스택 주소 등을 임의로 프로세서의 주소 공간에 배치하는 것이다.[29] 함수와 변수를 찾을 수 있는 가상 메모리 주소의 난수화로 버퍼 오버플로 이용이 더 어려워지지만 불가능한 것은 아니다.[29] 공격자가 이용 시도를 각각의 시스템에 따라 다르게 하도록 강요하므로 인터넷 웜 방어에 더 유용하다.[29]ASLR은 버퍼 오버플로 공격의 발전 형태인 Return-to-libc 공격을 완화할 수 있지만, Return-oriented programming에는 대응할 수 없다. 커널 공간의 ASLR을 '''KASLR''' (kernel address space layout randomization)이라고 하며, 리눅스 커널(버전 3.14 이후)[69], iOS(버전 6 이후)[70] 등에서 구현되어 있다.
gcc와 g++로 컴파일할 때 스택 영역과 힙 영역에 대해서는 ASLR을 사용하지만, 코드 영역에 ASLR을 사용하려면 옵션 "-pie"를 사용해야 한다.[71] 또한 공유 라이브러리에 ASLR을 사용하려면 옵션 "-fPIC"를 지정한다.[71]
4. 7. 심층 패킷 조사
심층 패킷 조사(DPI)는 네트워크 경계에서 공격 고유 신호와 휴리스틱을 사용하여 버퍼 오버플로를 악용하려는 매우 기본적인 원격 시도를 탐지한다. 이 기술은 알려진 공격의 시그니처를 가진 패킷을 차단할 수 있다. 이전에는 긴 일련의 No-Operation 명령(NOP-sled)이 감지되면 패킷을 막을 수 있었으며, 페이로드 위치가 약간 가변적인 상황에서 사용되었다.패킷 스캔은 알려진 공격만 방지할 수 있고 NOP 슬라이드는 다양한 방법으로 암호화 할 수 있기 때문에 효과적이지 못한 방법이다. 공격자들은 경험론적 패킷 스캐너와 침입 감지 시스템의 탐지를 회피하기 위하여 영숫자 코드, 변형, 또는 자기 수정 방식의 셸코드를 사용하기 시작하였다.
5. 이용의 역사
버퍼 오버플로는 1972년 컴퓨터 보안 기술 기획 연구에서 처음 소개된 이후로 알려진 개념이다.[32] 당시 연구에서는 "이 기능을 수행하는 코드는 출처와 목적지 주소를 적절히 검사하지 않으므로, 사용자가 모니터(오늘날의 커널)의 일부를 덮어쓰도록 허용한다. 이는 코드를 모니터에 삽입하여 제어권을 획득하는 데 사용될 수 있다."라고 설명했다.[81]
1980년대 개인용 컴퓨터가 확산되면서 버퍼 오버플로에 대한 인식이 높아졌다. 초기 마이크로소프트 윈도우 운영 체제는 버퍼 보호에 취약하여 많은 프로그래머들이 버퍼 오버플로를 인지하게 되었다.
최초로 문서화된 악의적인 버퍼 오버플로 이용은 1988년 모리스 웜이다.[33] 모리스 웜은 finger 서비스의 취약점을 이용했다.[81] 1995년 토마스 로파틱은 버퍼 오버플로를 독립적으로 재발견하고 버그트랙에 발표했다.[34][82] 1996년 엘리아스 레비는 프랙에 스택 기반 버퍼 오버플로 공격 방법을 단계별로 소개했다.[35][83]
이후, 2001년 코드 레드 웜은 인터넷 정보 서비스 5.0의 버퍼 오버플로를,[36][84] 2003년 SQL 슬래머 웜은 마이크로소프트 SQL 서버 2000의 버퍼 오버플로를 이용했다.[37][85]
2003년에는 엑스박스 게임, 플레이스테이션 2, 닌텐도 위 등 게임 콘솔에서도 버퍼 오버플로 취약점을 이용한 공격이 발생했다.[38][86]
6. 한국의 정보 보안 환경
참조
[1]
웹사이트
CORE-2007-0219: OpenBSD's IPv6 mbufs remote kernel buffer overflow
http://www.securityf[...]
2007-05-15
[2]
웹사이트
Modern Overflow Targets
http://packetstormse[...]
2013-07-05
[3]
웹사이트
The Metasploit Opcode Database
https://web.archive.[...]
2007-05-15
[4]
웹사이트
Microsoft Technet Security Bulletin MS04-028
https://web.archive.[...]
2007-05-15
[5]
웹사이트
Creating Arbitrary Shellcode In Unicode Expanded Strings
https://web.archive.[...]
2007-05-15
[6]
Webarchive
Buffer Overflows article on OWASP
https://www.owasp.or[...]
2016-08-29
[7]
웹사이트
vector::at - C++ Reference
http://www.cplusplus[...]
Cplusplus.com
2014-03-27
[8]
웹사이트
Archived copy
https://web.archive.[...]
2022-06-06
[9]
웹사이트
The Better String Library
http://bstring.sf.ne[...]
[10]
웹사이트
The Vstr Homepage
https://web.archive.[...]
2007-05-15
[11]
웹사이트
The Erwin Homepage
http://www.theiling.[...]
2007-05-15
[12]
웹사이트
Information technology – Programming languages, their environments and system software interfaces – Extensions to the C library – Part 1: Bounds-checking interfaces
https://www.iso.org/[...]
2007
[13]
웹사이트
CERT Secure Coding Initiative
https://archive.toda[...]
2007-07-30
[14]
웹사이트
Libsafe at FSF.org
http://directory.fsf[...]
2007-05-20
[15]
웹사이트
StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al.
https://www.usenix.o[...]
2007-05-20
[16]
웹사이트
ProPolice at X.ORG
https://web.archive.[...]
2007-05-20
[17]
웹사이트
Bypassing Windows Hardware-enforced Data Execution Prevention
https://web.archive.[...]
2007-05-20
[18]
간행물
Buffer overflow and format string overflow vulnerabilities
https://onlinelibrar[...]
2003-04-25
[19]
웹사이트
12th USENIX Security Symposium – Technical Paper
http://www.usenix.or[...]
2018-04-03
[20]
웹사이트
Protecting against Pointer Subterfuge (Kinda!)
https://web.archive.[...]
2018-04-03
[21]
웹사이트
USENIX - The Advanced Computing Systems Association
http://www.usenix.or[...]
2018-04-03
[22]
웹사이트
Protecting against Pointer Subterfuge (Redux)
https://web.archive.[...]
2018-04-03
[23]
웹사이트
PaX: Homepage of the PaX team
http://pax.grsecurit[...]
2007-06-03
[24]
웹사이트
KernelTrap.Org
https://archive.toda[...]
2007-06-03
[25]
웹사이트
Openwall Linux kernel patch 2.4.34-ow1
https://web.archive.[...]
2007-06-03
[26]
웹사이트
Microsoft Technet: Data Execution Prevention
https://web.archive.[...]
2006-06-30
[27]
웹사이트
BufferShield: Prevention of Buffer Overflow Exploitation for Windows
http://www.sys-manag[...]
2007-06-03
[28]
웹사이트
NGSec Stack Defender
http://www.ngsec.com[...]
2007-06-03
[29]
웹사이트
PaX at GRSecurity.net
http://pax.grsecurit[...]
2007-06-03
[30]
웹사이트
The Exploitant - Security info and tutorials
http://raykoid666.wo[...]
2009-11-29
[31]
간행물
Statically Detecting Likely Buffer Overflow Vulnerabilities
https://www.usenix.o[...]
2001-08-13
[32]
웹사이트
Computer Security Technology Planning Study
https://web.archive.[...]
2007-11-02
[33]
웹사이트
"A Tour of The Worm" by Donn Seeley, University of Utah
http://world.std.com[...]
2007-06-03
[34]
웹사이트
Bugtraq security mailing list archive
http://www.security-[...]
2007-06-03
[35]
웹사이트
"Smashing the Stack for Fun and Profit" by Aleph One
http://www.phrack.co[...]
2012-09-05
[36]
웹사이트
eEye Digital Security
https://web.archive.[...]
2007-06-03
[37]
웹사이트
Microsoft Technet Security Bulletin MS02-039
https://web.archive.[...]
2007-06-03
[38]
웹사이트
Hacker breaks Xbox protection without mod-chip
http://www.gamesindu[...]
2007-06-03
[39]
웹사이트
第10章 著名な脆弱性対策バッファオーバーフロー: #1 概要
https://www.ipa.go.j[...]
정보처리추진기구
2018-12-14
[40]
웹사이트
'[迷信] scanf ではバッファオーバーランを防げない'
http://www.kijineko.[...]
주식회사키지네코
2010-02-28
[41]
웹사이트
CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
https://cwe.mitre.or[...]
Mitre
2018-12-18
[42]
웹사이트
CWE-121: Stack-based Buffer Overflow
https://cwe.mitre.or[...]
Mitre
2018-12-18
[43]
웹사이트
CWE-122: Heap-based Buffer Overflow
https://cwe.mitre.or[...]
Mitre
2018-12-18
[44]
웹사이트
CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
https://cwe.mitre.or[...]
Mitre
2018-12-18
[45]
웹사이트
CWE-787: Out-of-bounds Write
https://cwe.mitre.or[...]
Mitre
2018-12-18
[46]
웹사이트
CWE-193: Off-by-one Error
https://cwe.mitre.or[...]
Mitre
2018-12-18
[47]
웹사이트
CWE-131: Incorrect Calculation of Buffer Size
https://cwe.mitre.or[...]
Mitre
2018-12-18
[48]
웹사이트
CWE-680: Integer Overflow to Buffer Overflow
https://cwe.mitre.or[...]
Mitre
2018-12-18
[49]
웹사이트
The Exploitant - Security info and tutorials
http://raykoid666.wo[...]
2009-11-29
[50]
간행물
STRIDE: Polymorphic Sled Detection through Instruction Sequence Analysis.
http://dcs.ics.forth[...]
IFIP International Information Security Conference
[51]
웹사이트
The Metasploit Opcode Database
https://web.archive.[...]
2007-05-15
[52]
웹사이트
malloc(3)のメモリ管理構造
https://www.valinux.[...]
VA Linux Systems Japan
2007-11-30
[53]
웹사이트
A Memory Allocator
http://g.oswego.edu/[...]
2018-12-26
[54]
웹사이트
CWE-123: Write-what-where Condition
https://cwe.mitre.or[...]
Mitre
2018-12-26
[55]
웹사이트
JVNDB-2015-004721 Silicon Integrated Systems WindowsXP Display Manager における権限を取得される脆弱性
https://jvndb.jvn.jp[...]
2018-12-26
[56]
웹사이트
第14回: バッファオーバーフローとサーバ側のセキュリティ対策を考える
https://thinkit.co.j[...]
2006-09-05
[57]
웹사이트
세큐어한 프로그래머 버퍼 오버플로에 대항하는 오늘 최대의 취약성을 방지한다
https://www.ibm.com/[...]
IBM
2004-01-27
[58]
웹사이트
StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al.
https://www.usenix.o[...]
2012-02-09
[59]
웹사이트
/GS (버퍼의 보안 체크)
https://docs.microso[...]
2016-11-04
[60]
웹사이트
Libsafe - Free Software Directory
http://directory.fsf[...]
2012-02-09
[61]
웹사이트
Windows XP Service Pack 2、Windows XP Tablet PC Edition 2005、및 Windows Server 2003의 데이터 실행 방지 (DEP) 기능의 상세
http://support.micro[...]
2012-02-17
[62]
웹사이트
Bypassing Windows Hardware-enforced Data Execution Prevention
http://www.uninforme[...]
2007-05-20
[63]
웹사이트
PaX: Homepage of the PaX team
http://pax.grsecurit[...]
2012-02-17
[64]
웹사이트
KernelTrap.Org
https://archive.is/2[...]
2012-02-17
[65]
웹사이트
Openwall Linux kernel patch 2.4.34-ow1
http://linux.softped[...]
2012-02-17
[66]
웹사이트
BufferShield: Prevention of Buffer Overflow Exploitation for Windows
http://www.sys-manag[...]
2012-02-17
[67]
웹사이트
NGSec Stack Defender
https://web.archive.[...]
2012-02-17
[68]
웹사이트
NX비트
http://e-words.jp/w/[...]
2019-01-01
[69]
웹사이트
Linux kernel 3.14, Section 1.7. Kernel address space randomization
https://kernelnewbie[...]
2014-03-30
[70]
웹사이트
iOS 6 Exploitation 280 Days Later
https://www.slidesha[...]
2019-01-01
[71]
웹사이트
Hardening
https://wiki.debian.[...]
Devian
2019-01-01
[72]
웹사이트
Delphi Basics : $RangeChecks command
http://www.delphibas[...]
2012-02-03
[73]
웹사이트
範囲チェック - RAD Studio
https://docwiki.emba[...]
[74]
웹사이트
第10章 著名な脆弱性対策 バッファオーバーフロー: #2 ソースコード記述時の対策
https://www.ipa.go.j[...]
情報処理推進機構
2018-12-27
[75]
웹사이트
The Better String Library
http://bstring.sf.ne[...]
2012-02-08
[76]
웹사이트
The Vstr Homepage
http://www.and.org/v[...]
2012-02-08
[77]
웹사이트
The Erwin Homepage
http://www.theiling.[...]
2012-02-08
[78]
웹사이트
第10章 著名な脆弱性対策 バッファオーバーフロー: #3 ソースコードの静的検査
https://www.ipa.go.j[...]
情報処理推進機構
2018-12-27
[79]
conference
Writing Metasploit Plugins: from vulnerability to exploit
http://conference.hi[...]
[80]
웹사이트
CTFで学ぶ脆弱性(スタックバッファオーバーフロー編・その1)
http://www.intellili[...]
NTTデータ先端技術株式会社
2019-01-01
[81]
웹사이트
"A Tour of The Worm" by Donn Seeley, University of Utah
http://world.std.com[...]
2007-06-03
[82]
웹사이트
Bugtraq security mailing list archive
http://www.security-[...]
2007-06-03
[83]
웹사이트
Smashing the Stack for Fun and Profit
http://www.phrack.or[...]
2007-06-03
[84]
웹사이트
eEye Digital Security
http://research.eeye[...]
2007-06-03
[85]
웹사이트
Microsoft Technet Security Bulletin MS02-039
http://www.microsoft[...]
2007-06-03
[86]
웹사이트
Hacker breaks Xbox protection without mod-chip
http://www.gamesindu[...]
2007-06-03
[87]
웹사이트
Routine Mappings(CRT)
http://msdn.microsof[...]
[88]
서적
Safer C: Developing Software for High-integrity and Safety-critical Systems
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com